#ifndef _IR_NODE_H
#define _IR_NODE_H

#include <cstdio>

class IRNode {
public:
    typedef enum { PLUS, MINUS, MULT, NUMBER, POW, MAX } nodetype;

    IRNode(nodetype t, IRNode *l, IRNode *r) : 
        type(t), value(-1), left(l), right(r) { }

    IRNode(unsigned v) : 
        type(NUMBER), value(v), left(0), right(0) { }

    unsigned eval() const
    {
        switch (type) 
        {
            case NUMBER: return value;
            case PLUS: return left->eval() + right->eval();
            case MINUS: return left->eval() - right->eval();
            case MULT: return left->eval() * right->eval();
            case POW: return testPow();
            case MAX: return testMax();
            default: throw "eval failed";
        }
    }

    unsigned testPow() const
    {
        int exponent = right->eval();
        int base = left->eval();
        int returnValue = 1;
        for (int i = 0; i < exponent; ++i)
            returnValue *= base;
        return returnValue;
    }

    unsigned testMax() const
    {
        int rightValue = right->eval();
        int levtValue = left->eval();

        return (rightValue > levtValue) ? rightValue : levtValue; 
    }

    void gen_expr_x86() const
    {
        fprintf(stderr, "generating x86 code\n");
        // output assembler source
        printf("\t.text\n"); // code follows...
        printf("\t.globl calc_expr\n"); // export symbol calc_expr
        printf("calc_expr:\n"); // function calc_expr starts here...
        gen_x86(); // generate code for expression
        printf("\tret\n"); // return from function
    }

    void gen_x86() const
    {
        switch(type)
        {
            case NUMBER: // load number in eax
                printf("\tmov\t$%d, %%eax\n", value);
                break;
            case PLUS: // add using stack and rbx
                left->gen_x86(); // compute left operand in eax
                printf("\tpush\t%%eax\n"); // store on stack
                right->gen_x86(); // compute right operand in eax
                printf("\tpop\t%%ebx\n"); // get operand1 from stack into ebx
                printf("\tadd\t%%ebx, %%eax\n"); // add ebx to eax, result in eax
                break;
            case MINUS:
                left->gen_x86(); // compute left operand in eax
                printf("\tpush\t%%eax\n"); // store on stack
                right->gen_x86(); // compute right operand in eax
                printf("\tpop\t%%ebx\n"); // get operand1 from stack into ebx
                printf("\tsub\t%%ebx, %%rax\n"); // add ebx to eax, result in eax
                break;
            case MULT:
                left->gen_x86(); // compute left operand in eax
                printf("\tpush\t%%eax\n"); // store on stack
                right->gen_x86(); // compute right operand in eax
                printf("\tpop\t%%ebx\n"); // get operand1 from stack into ebx
                printf("\timul\t%%ebx, %%eax\n"); // add ebx to eax, result in eax

                break;
            case POW:
                left->gen_x86();
                printf("\tpush\t%%eax\n");
                right->gen_x86();
                printf("\tpop\t%%ebx\n");

                printf("\tmov\t$0,\t%%edx\n"); // mov 0 to some register
                printf("\tcmp\t%%eax,\t%%edx\n"); // test if regitster
                printf("\tjnb\tgreater_than\n"); // jump if > 0
                printf("\tmov\t$-1,\t%%eax\n");
                printf("\tret\n");
                printf("greater_than:\n");
                printf("\tmov\t%%eax, %%ecx\n");
                printf("\tmov\t$1,\t%%eax\n");
                printf("loop_head:\n");
                printf("\timul\t%%ebx\n"); 
                printf("\tloop\tloop_head\n"); // loop back to head

                break;
            case MAX:
                left->gen_x86(); // compute left operand in eax
                printf("\tpush\t%%eax\n"); // store on stack
                right->gen_x86(); // compute right operand in eax
                printf("\tpop\t%%ebx\n"); // get operand1 from stack into ebx
                printf("\tcmp\t%%eax,\t%%ebx\n");
                printf("\tjge\tgreater_equal\n");
                printf("\tret\n");

                printf("greater_equal:\n");
                printf("\tgreater_equal:\n");
                printf("\tmov\t%%ebx,\t%%eax\n");
                printf("\tret\n");                
                break;
        }
    }

    nodetype type;
    unsigned value;
    IRNode *left, *right;
};

#endif /* _IR_NODE_H */
